from flightanalysis import Section, get_schedule, Box
from flightdata import Flight
from flightplotting.plots import plotsec, plotdtw
import plotly
plotly.offline.init_notebook_mode()
The first task is to read the flight log file to a Section instance. A Section represents the core flight data - position, orientation, velicity etc. The section is generally (but doesnt have to be) standardised to a box coordinate frame, with the origin on the pilot position, Y axis pointing towards centre, Z up, right axis out pilots right shoulder. The takeoff and landing are chopped off so only the scored flight remains.
flight = Flight.from_csv("examples/logs/00000036.csv")
box = Box.from_f3a_zone("examples/logs/box.f3a")
flown = Section.from_flight(flight, box).subset(150, 576)
The sequence flown is read into a Schedule instance. This contains a list of manoeuvres, with each manoeuvre containing a list of elements (Line, Loop, Snap, Spin, StallTurn). Each eleemnt also contains some basic scaling information, length for lines, radius for loops etc. The Schedule class contains methods return scaled copies of itself. The recorded flight was flown roughly at 170m depth, so the scale_distance method works here. It also contains methods to create a Section instance with template data. This section object is special because all the manoeuvres and elements are labelled.
p23 = get_schedule("F3A", "P23")
template = p23.scale_distance(170).create_raw_template("left", 30.0, 170.0)
plotsec(template.subset(0,85), nmodels=20, scale=10).update_layout(width=600, height=400)
We want to transfer these labels from the template to the flown data. The align method on the section class performs dynamic time warping based on the axis rate (roll, pitch and yaw). Dynamic time warping gives a measure of the distance between two time series that may differ in the time axis. It also gives a path, to map the indeces from one to the other. As long as the template has been roughly scaled to the flight, and the flight was flown reasonably well this should work.
from geometry import Point
dist, aligned = Section.align(flown, template, 2 )
manid = 0
#fig = plotdtw(p23.manoeuvres[manid].get_data(aligned), p23.manoeuvres[manid].elements).update_layout(width=800, height=500)
els = [el for man in p23.manoeuvres[:4] for el in man.elements]
print(len(els))
fig = plotdtw(p23.get_subset(aligned, 0,4), p23.manoeuvres[:4]).update_layout(width=800, height=500)
fig.show()
44
Now the match_intention method goes through all the elements of the aligned data and adjusts the element scaling to sut. The correct_intention method then adjusts the scaled elements to reflect the judging criteria (not yet all of it).
intended_p23 = p23.match_intention(aligned)
corrected_p23 = intended_p23.correct_intention()
intended_template = intended_p23.create_elm_matched_template(aligned)
corrected_template = corrected_p23.create_man_matched_template(aligned)
manid = 10
#fig = plotsec(p23.get_subset(aligned,0,4), nmodels = 0,scale=2, color="blue", ribb=True)
#fig.add_traces(plotsec(p23.get_subset(intended_template,0,4),scale=2, color="orange", nmodels = 0, ribb=True).data)
fig = plotsec(p23.get_subset(intended_template,0,4),scale=2, nmodels = 0, ribb=True)
fig.update_layout(width=600, height=400)
elm = p23.manoeuvres[10].elements[6]
flown = elm.get_data(aligned)
template = elm.get_data(intended_template)
import plotly.graph_objects as go
from flightplotting.plots import aiaa_layout,axis
fig = go.Figure()
fig.add_trace(go.Scatter(x=flown.x, y=flown.z, line=dict(color="black", dash="solid"), name="flown"))
fig.add_trace(go.Scatter(x=template.x, y=template.z, line=dict(color="black", dash="dash"), name="template"))
fig.update_layout(yaxis=dict(scaleanchor="x", title="height (m)"), xaxis=dict(title="x position (m)"), width=800, height=600)
fig.update_xaxes(**axis)
fig.update_yaxes(**axis)
fig.update_layout(**aiaa_layout)
fig
import plotly.express as px
from geometry import Points
from scipy import optimize
import numpy as np
x, y = flown.pos.x, flown.pos.z
def calc_R(xc, yc): return np.sqrt((x-xc)**2 + (y-yc)**2)
def f_2(c):
Ri = calc_R(*c)
return Ri - Ri.mean()
center, ier = optimize.leastsq(f_2, (np.mean(x), np.mean(y)))
diameter=2 * calc_R(*center).mean()
rolls=np.sign(np.mean(Points.from_pandas(flown.brvel).x)) * abs(elm.rolls)
center = Point(center[0], flown.pos.y.iloc[0], center[1] )
diameter, center.to_list()
(175.39009987829522, [131.24174808919096, 201.18383565163447, 163.24171542186494])
def rad_pos(pin):
p = pin*Point(1,0,1) - center * Point(1,0,1)
return np.arctan2(p.z, p.x)
import plotly.graph_objects as go
from geometry import Points, Quaternions
x=-np.degrees(rad_pos(flown.gpos))
x = x - x[0]
flwn_radius = abs(flown.gpos * Point(1,0,1) - center* Point(1,0,1))
flwn_wvel = flown.gatt.transform_point(flown.gbvel)
flwn_angle = np.degrees(np.arcsin(flwn_wvel.y / abs(flown.gbvel)))
nw_beta = np.arctan2(flown.gbvel.y, flown.gbvel.x)
yaw_removed = Quaternions.from_euler(Points.Z(-nw_beta)).transform_point(flown.gatt.inverse().transform_point(Point(0,1,0)))
flwn_roll = np.degrees(np.arctan2(yaw_removed.z , yaw_removed.y))
tr_rad_flwn = go.Scatter(x=x, y=flwn_radius, line=dict(color="black", dash="solid"),name="flown", xaxis="x", yaxis="y")
tr_rad_temp = go.Scatter(x=x, y=np.full(len(x), diameter/2), line=dict(color="black", dash="dash"),name="template", xaxis="x", yaxis="y")
tr_ang_flwn = go.Scatter(x=x, y=flwn_angle, line=dict(color="black", dash="solid"),name="flown", xaxis="x", yaxis="y2", showlegend=False)
tr_ang_temp = go.Scatter(x=x, y=np.zeros(len(x)), marker=None, line=dict(color="black", dash="dash"),name="template", xaxis="x", yaxis="y2", showlegend=False)
tr_roll_flwn = go.Scatter(x=x, y=flwn_roll, line=dict(color="black", dash="solid"),name="flown", xaxis="x", yaxis="y3", showlegend=False)
tr_roll_temp = go.Scatter(x=x, y=np.zeros(len(x)), line=dict(color="black", dash="dash"),name="template", xaxis="x", yaxis="y3", showlegend=False)
data = [tr_rad_flwn,tr_rad_temp,tr_ang_flwn,tr_ang_temp, tr_roll_flwn, tr_roll_temp]
layout = go.Layout(
xaxis=dict(dict(title="radial position, deg", range=(0, x[-1])), anchor="y3", **axis),
yaxis=dict(title="loop radius, m", range=(80,100), domain=[0.66, 1.0], **axis),
yaxis2 = dict(title="heading error, deg", range=(-15,15), domain=[0.36, 0.63], **axis),
yaxis3 = dict(title="roll error, deg", range=(-30,30), domain=[0.0, 0.3], **axis),
#legend=dict(yanchor="bottom", xanchor="left", x=0.05, y=0.02),
width=800, height=600,
**aiaa_layout
)
fig= go.Figure(data, layout)
fig